/*******************************************************************************
 * Copyright 2011 Towee.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package net.towee.client.history;

import java.util.HashMap;
import java.util.Map;

import net.towee.client.com.app.AppController.Component;
import net.towee.client.event.core.ShowComponentEvent;
import net.towee.client.event.core.ShowComponentEventHandler;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.History;
import com.google.inject.Inject;

/**
 * This manager parse history token when it changed. Every
 * history token have structure:
 * #component:{component_name};{some_key}:{some_value};...
 * 
 * @author viiv.c58@gmail.com (Artem Artem'ev)
 */
public class HistoryManager implements ValueChangeHandler<String> {
	private Map<Component, HandlerManager> handlers = new HashMap<Component, HandlerManager>();
	private final HandlerManager eventBus;
	
	/*
	 * Needed when some component showed by ShomComponentEvent for
	 * not fire the same event on history value changed.
	 */
	private boolean dontFire = false;
	private boolean dontHandle = false;
	
	/**
	 * Define it object as ValueChangeHandler
	 */
	@Inject
	public HistoryManager(HandlerManager eventBus){
		// Create handler managers for each component
		for(Component com : Component.values()) {
			handlers.put( com, new HandlerManager(null) );
		}
		
		// Set event bus and history handler
		this.eventBus = eventBus;
		History.addValueChangeHandler(this);
		
		// Handle change active component
		this.eventBus.addHandler(ShowComponentEvent.TYPE, new ShowComponentEventHandler(){
			@Override
			public void onShowComponent(Component component, String... params) {
				// If we dont need to handle this event
				if(dontHandle) {
					dontHandle = false;
					return;
				}
				
				// Create string for new history item
				StringBuffer histStr = new StringBuffer("component:"+component.toString().toLowerCase()+";");
				
				if(params != null) {
					// Dont fire show component event
					dontFire = true;
					
					// Add arguments
					for(int i=0;i<params.length;i+=2)
						histStr.append(params[i]+":"+params[i+1]+";");
				}
				
				// Add new history item
				History.newItem(histStr.toString(), params != null);
			}
		});
	}
	
	public void addHistoryItem(Component com, String... params) {
		// Create string for new history item
		StringBuffer histStr = new StringBuffer("component:"+com.toString().toLowerCase()+";");
		
		// Add parameters
		if(params != null) {
			for(int i=0;i<params.length;i+=2)
				histStr.append(params[i]+":"+params[i+1]+";");
		}
		
		// Add new history item
		History.newItem(histStr.toString(), false);
	}
	
	/**
	 * Add new history handler for specified component
	 * @param component
	 * @param handler
	 */
	public HandlerRegistration addHandler(Component component, HistoryHandler handler) {
		return handlers.get(component).addHandler(HistoryEvent.TYPE, handler);
	}

	/**
	 * Invoked when value changed.
	 */
	@Override
	public void onValueChange(ValueChangeEvent<String> event) {
		Map<String, String> args = new HashMap<String, String>();
		
		// Get tokens
		String token = URL.decode(event.getValue());
		String[] tokens = token.split(";");
		
		// Parse parameters
		int delPos = 0;
		String name, value, component = null;
		
		for(int i=0;i<tokens.length;i++) {
			delPos = tokens[i].indexOf(":");
			
			if(delPos > 0) {
				name  = tokens[i].substring(0, delPos);
				value = tokens[i].substring(delPos+1);
				
				if(name.equals("component")) 
					component = value;
				else
					args.put(name, value);
			}
		}
		
		// If request component not exists do nothing
		if(component == null || Component.valueOf(component.toUpperCase()) == null)
			return;
		
		if(!dontFire) {
			// We don't need to handler new event
			dontHandle = true;
			
			// Show component before invoking handler
			eventBus.fireEvent(new ShowComponentEvent( Component.valueOf(component.toUpperCase()) ));
		}	
		else
			dontFire = false;
		
		
		// Invoke handler if it defined
		if(component != null && handlers.containsKey( Component.valueOf(component.toUpperCase()) ))
			handlers.get( Component.valueOf(component.toUpperCase()) ).fireEvent( new HistoryEvent( args ) );
	}
}